import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import axios from 'axios'
import { ref } from 'vue'

import router from '@/router'
import pinia from '@/stores'
import { ElNotification, ElMessage } from 'element-plus'
import { useAuthStore } from '@/stores/auth'

const authStore = useAuthStore(pinia)


// 数据返回的接口
// 定义请求响应参数，不含data

export const BASE_URL: string = 'http://localhost:8901'

const whiteList = ['/login']


enum RequestEnums {
  TIMEOUT = 20000,
  OVERDUE = 401, // 登录失效
  SUCCESS = 200, // 请求成功
  VALID_ERR = 438, // 数据校验异常
}

export interface Result {
  code: number;
  message: string;
  success: boolean;
}

// 请求响应参数，包含data
export interface ResultData<T = any> extends Result {
  data: T;
}

const config = {
  // 默认地址
  baseURL: BASE_URL as string,
  // 设置超时时间
  timeout: RequestEnums.TIMEOUT as number,
  // 跨域时候允许携带凭证
  withCredentials: true
}

class RequestHttp {
  // 定义成员变量并指定类型
  service: AxiosInstance

  public constructor(config: AxiosRequestConfig) {
    // 实例化axios
    this.service = axios.create(config)

    /**
     * 请求拦截器
     * 客户端发送请求 -> [请求拦截器] -> 服务器
     * token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
     */
    this.service.interceptors.request.use(
      (config: any) => {
        const token = ref<string | null>()
        if (!whiteList.includes(config.url)) {
          token.value = authStore.accessToken
        }
        return {
          ...config,
          headers: {
            token: token.value // 请求头中携带token信息
          }
        }
      },
      (error: AxiosError) => {
        // 请求报错
        Promise.reject(error)
      }
    )

    /**
     * 响应拦截器
     * 服务器换返回信息 -> [拦截统一处理] -> 客户端ts获取到信息
     */
    this.service.interceptors.response.use(
      (response: AxiosResponse) => {
        const { data, config } = response // 解构
        if (response.status != 200) {
          return data;
        }
        if (data.code === RequestEnums.OVERDUE) {
          // 登录信息失效，应跳转到登录页面，并清空本地的token
          router.push({ path: '/login' }).then(() => null)
          authStore.setToken(null)
          ElNotification.error({
            title: '认证错误',
            message: data.message,
            type: 'error'
          })
          return Promise.reject(data)
        }
        if (data.code === RequestEnums.VALID_ERR) {
          ElMessage({
            message: '数据校验异常，请检查！',
            type: 'warning',
          })
          return Promise.reject(data)
        }
        // 全局错误信息拦截（防止下载文件得时候返回数据流，没有code，直接报错）
        if (data.code && data.code !== RequestEnums.SUCCESS) {
          ElNotification.error({
            title: "错误",
            message: data.message,
            type: 'error'
          })
          return Promise.reject(data)
        }
        return data
      },
      (error: AxiosError) => {
        ElNotification.error({
          title: '全局异常',
          message: error.message,
          type: 'error'
        })
        const data: ResultData = { data: undefined, code: 400, message: '全局异常', success: false }
        return Promise.reject(data)
      }
    )
  }

  // 常用方法封装
  get<T>(url: string, params?: object): Promise<ResultData<T>> {
    return this.service.get(url, { params })
  }

  post<T>(url: string, params?: object, config?: AxiosRequestConfig): Promise<ResultData<T>> {
    return this.service.post(url, params, config)
  }

  put<T>(url: string, params?: object): Promise<ResultData<T>> {
    return this.service.put(url, params)
  }

  delete<T>(url: string, params?: object): Promise<ResultData<T>> {
    return this.service.delete(url, { params })
  }

  upload<T>(url: string, param: FormData): Promise<ResultData<T>> {
    const config: AxiosRequestConfig = {
      headers: { 'Content-Type': 'multipart/form-data' }
    }
    return this.service.post(url, param, config)
  }

  download(url: string, param?: object) : Promise<Uint8Array> {
    const config: AxiosRequestConfig = {
      responseType: 'blob'
    }
    return this.service.post(url, param, config)
  }
}

// 导出一个实例对象
export default new RequestHttp(config)